1   /*                        __    __  __  __    __  ___
2    *                       \  \  /  /    \  \  /  /  __/
3    *                        \  \/  /  /\  \  \/  /  /
4    *                         \____/__/  \__\____/__/.ɪᴏ
5    * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
6    */
7   package io.vavr.collection;
8   
9   import io.vavr.*;
10  import io.vavr.control.Option;
11  import io.vavr.control.Try;
12  import org.junit.Ignore;
13  import org.junit.Test;
14  
15  import java.io.InvalidObjectException;
16  import java.math.BigDecimal;
17  import java.util.ArrayList;
18  import java.util.Spliterator;
19  import java.util.function.Function;
20  import java.util.function.Predicate;
21  import java.util.function.Supplier;
22  import java.util.stream.Collector;
23  
24  import static io.vavr.collection.Stream.concat;
25  
26  public class StreamTest extends AbstractLinearSeqTest {
27  
28      // -- construction
29  
30      @Override
31      protected <T> Collector<T, ArrayList<T>, Stream<T>> collector() {
32          return Stream.collector();
33      }
34  
35      @Override
36      protected <T> Stream<T> empty() {
37          return Stream.empty();
38      }
39  
40      @Override
41      protected <T> Stream<T> of(T element) {
42          return Stream.of(element);
43      }
44  
45      @SuppressWarnings("varargs")
46      @SafeVarargs
47      @Override
48      protected final <T> Stream<T> of(T... elements) {
49          return Stream.of(elements);
50      }
51  
52      @Override
53      protected <T> Stream<T> ofAll(Iterable<? extends T> elements) {
54          return Stream.ofAll(elements);
55      }
56  
57      @Override
58      protected <T extends Comparable<? super T>> Stream<T> ofJavaStream(java.util.stream.Stream<? extends T> javaStream) {
59          return Stream.ofAll(javaStream);
60      }
61  
62      @Override
63      protected Stream<Boolean> ofAll(boolean... elements) {
64          return Stream.ofAll(elements);
65      }
66  
67      @Override
68      protected Stream<Byte> ofAll(byte... elements) {
69          return Stream.ofAll(elements);
70      }
71  
72      @Override
73      protected Stream<Character> ofAll(char... elements) {
74          return Stream.ofAll(elements);
75      }
76  
77      @Override
78      protected Stream<Double> ofAll(double... elements) {
79          return Stream.ofAll(elements);
80      }
81  
82      @Override
83      protected Stream<Float> ofAll(float... elements) {
84          return Stream.ofAll(elements);
85      }
86  
87      @Override
88      protected Stream<Integer> ofAll(int... elements) {
89          return Stream.ofAll(elements);
90      }
91  
92      @Override
93      protected Stream<Long> ofAll(long... elements) {
94          return Stream.ofAll(elements);
95      }
96  
97      @Override
98      protected Stream<Short> ofAll(short... elements) {
99          return Stream.ofAll(elements);
100     }
101 
102     @Override
103     protected <T> Stream<T> tabulate(int n, Function<? super Integer, ? extends T> f) {
104         return Stream.tabulate(n, f);
105     }
106 
107     @Override
108     protected <T> Stream<T> fill(int n, Supplier<? extends T> s) {
109         return Stream.fill(n, s);
110     }
111 
112     @Override
113     protected Stream<Character> range(char from, char toExclusive) {
114         return Stream.range(from, toExclusive);
115     }
116 
117     @Override
118     protected Stream<Character> rangeBy(char from, char toExclusive, int step) {
119         return Stream.rangeBy(from, toExclusive, step);
120     }
121 
122     @Override
123     protected Stream<Double> rangeBy(double from, double toExclusive, double step) {
124         return Stream.rangeBy(from, toExclusive, step);
125     }
126 
127     @Override
128     protected Stream<Integer> range(int from, int toExclusive) {
129         return Stream.range(from, toExclusive);
130     }
131 
132     @Override
133     protected Stream<Integer> rangeBy(int from, int toExclusive, int step) {
134         return Stream.rangeBy(from, toExclusive, step);
135     }
136 
137     @Override
138     protected Stream<Long> range(long from, long toExclusive) {
139         return Stream.range(from, toExclusive);
140     }
141 
142     @Override
143     protected Stream<Long> rangeBy(long from, long toExclusive, long step) {
144         return Stream.rangeBy(from, toExclusive, step);
145     }
146 
147     @Override
148     protected Stream<Character> rangeClosed(char from, char toInclusive) {
149         return Stream.rangeClosed(from, toInclusive);
150     }
151 
152     @Override
153     protected Stream<Character> rangeClosedBy(char from, char toInclusive, int step) {
154         return Stream.rangeClosedBy(from, toInclusive, step);
155     }
156 
157     @Override
158     protected Stream<Double> rangeClosedBy(double from, double toInclusive, double step) {
159         return Stream.rangeClosedBy(from, toInclusive, step);
160     }
161 
162     @Override
163     protected Stream<Integer> rangeClosed(int from, int toInclusive) {
164         return Stream.rangeClosed(from, toInclusive);
165     }
166 
167     @Override
168     protected Stream<Integer> rangeClosedBy(int from, int toInclusive, int step) {
169         return Stream.rangeClosedBy(from, toInclusive, step);
170     }
171 
172     @Override
173     protected Stream<Long> rangeClosed(long from, long toInclusive) {
174         return Stream.rangeClosed(from, toInclusive);
175     }
176 
177     @Override
178     protected Stream<Long> rangeClosedBy(long from, long toInclusive, long step) {
179         return Stream.rangeClosedBy(from, toInclusive, step);
180     }
181 
182     @Override
183     @SuppressWarnings("unchecked")
184     protected <T> Stream<Stream<T>> transpose(Seq<? extends Seq<T>> rows) {
185         return Stream.transpose((Stream<Stream<T>>) rows);
186     }
187 
188     @Override
189     protected boolean useIsEqualToInsteadOfIsSameAs() {
190         return true;
191     }
192 
193     // -- static concat()
194 
195     @Test
196     public void shouldConcatEmptyIterableIterable() {
197         final Iterable<Iterable<Integer>> empty = List.empty();
198         assertThat(concat(empty)).isSameAs(empty());
199     }
200 
201     @Test
202     public void shouldConcatNonEmptyIterableIterable() {
203         final Iterable<Iterable<Integer>> itIt = List.of(List.of(1, 2), List.of(3));
204         assertThat(concat(itIt)).isEqualTo(of(1, 2, 3));
205     }
206 
207     @Test
208     public void shouldConcatEmptyArrayIterable() {
209         assertThat(concat()).isSameAs(empty());
210     }
211 
212     @Test
213     public void shouldConcatNonEmptyArrayIterable() {
214         assertThat(concat(List.of(1, 2), List.of(3))).isEqualTo(of(1, 2, 3));
215     }
216 
217     // -- static from(int)
218 
219     @Test
220     public void shouldGenerateIntStream() {
221         assertThat(Stream.from(-1).take(3)).isEqualTo(Stream.of(-1, 0, 1));
222     }
223 
224     @Test
225     public void shouldGenerateOverflowingIntStream() {
226         //noinspection NumericOverflow
227         assertThat(Stream.from(Integer.MAX_VALUE).take(2))
228                 .isEqualTo(Stream.of(Integer.MAX_VALUE, Integer.MAX_VALUE + 1));
229     }
230 
231     // -- static from(int, int)
232 
233     @Test
234     public void shouldGenerateIntStreamWithStep() {
235         assertThat(Stream.from(-1, 6).take(3)).isEqualTo(Stream.of(-1, 5, 11));
236     }
237 
238     @Test
239     public void shouldGenerateOverflowingIntStreamWithStep() {
240         //noinspection NumericOverflow
241         assertThat(Stream.from(Integer.MAX_VALUE, 2).take(2))
242                 .isEqualTo(Stream.of(Integer.MAX_VALUE, Integer.MAX_VALUE + 2));
243     }
244 
245     // -- static from(long)
246 
247     @Test
248     public void shouldGenerateLongStream() {
249         assertThat(Stream.from(-1L).take(3)).isEqualTo(Stream.of(-1L, 0L, 1L));
250     }
251 
252     @Test
253     public void shouldGenerateOverflowingLongStream() {
254         //noinspection NumericOverflow
255         assertThat(Stream.from(Long.MAX_VALUE).take(2))
256                 .isEqualTo(Stream.of(Long.MAX_VALUE, Long.MAX_VALUE + 1));
257     }
258 
259     // -- static from(long, long)
260 
261     @Test
262     public void shouldGenerateLongStreamWithStep() {
263         assertThat(Stream.from(-1L, 5L).take(3)).isEqualTo(Stream.of(-1L, 4L, 9L));
264     }
265 
266     @Test
267     public void shouldGenerateOverflowingLongStreamWithStep() {
268         //noinspection NumericOverflow
269         assertThat(Stream.from(Long.MAX_VALUE, 2).take(2))
270                 .isEqualTo(Stream.of(Long.MAX_VALUE, Long.MAX_VALUE + 2));
271     }
272     // -- static continually(Supplier)
273 
274     @Test
275     public void shouldGenerateInfiniteStreamBasedOnSupplier() {
276         assertThat(Stream.continually(() -> 1).take(13).reduce((i, j) -> i + j)).isEqualTo(13);
277     }
278 
279     // -- static iterate(T, Function)
280 
281     @Test
282     public void shouldGenerateInfiniteStreamBasedOnSupplierWithAccessToPreviousValue() {
283         assertThat(Stream.iterate(2, (i) -> i + 2).take(3).reduce((i, j) -> i + j)).isEqualTo(12);
284     }
285 
286     // -- static continually (T)
287 
288     @Test
289     public void shouldGenerateInfiniteStreamBasedOnRepeatedElement() {
290         assertThat(Stream.continually(2).take(3).reduce((i, j) -> i + j)).isEqualTo(6);
291     }
292 
293     // -- static cons(T, Supplier)
294 
295     @Test
296     public void shouldBuildStreamBasedOnHeadAndTailSupplierWithAccessToHead() {
297         assertThat(Stream.cons(1, () -> Stream.cons(2, Stream::empty))).isEqualTo(Stream.of(1, 2));
298     }
299 
300     // -- static narrow
301 
302     @Test
303     public void shouldNarrowStream() {
304         final Stream<Double> doubles = of(1.0d);
305         final Stream<Number> numbers = Stream.narrow(doubles);
306         final int actual = numbers.append(new BigDecimal("2.0")).sum().intValue();
307         assertThat(actual).isEqualTo(3);
308     }
309 
310     // -- append
311 
312     @Test
313     public void shouldAppendMillionTimes() {
314         final int bigNum = 1_000_000;
315         assertThat(Stream.range(0, bigNum).foldLeft(Stream.empty(), Stream::append).length()).isEqualTo(bigNum);
316     }
317 
318     // -- appendAll
319 
320     @Test
321     public void shouldAppendAll() {
322         assertThat(of(1, 2, 3).appendAll(of(4, 5, 6))).isEqualTo(of(1, 2, 3, 4, 5, 6));
323     }
324 
325     @Test
326     public void shouldAppendAllIfThisIsEmpty() {
327         assertThat(empty().appendAll(of(4, 5, 6))).isEqualTo(of(4, 5, 6));
328     }
329 
330     @Test
331     public void shouldAppendAllIfThatIsInfinite() {
332         assertThat(of(1, 2, 3).appendAll(Stream.from(4)).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6));
333     }
334 
335     @Test
336     public void shouldAppendAllToInfiniteStream() {
337         assertThat(Stream.from(1).appendAll(Stream.continually(() -> -1)).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6));
338     }
339 
340     // -- combinations
341 
342     @Test
343     public void shouldComputeCombinationsOfEmptyStream() {
344         assertThat(Stream.empty().combinations()).isEqualTo(Stream.of(Stream.empty()));
345     }
346 
347     @Test
348     public void shouldComputeCombinationsOfNonEmptyStream() {
349         assertThat(Stream.of(1, 2, 3).combinations()).isEqualTo(Stream.of(Stream.empty(), Stream.of(1), Stream.of(2),
350                 Stream.of(3), Stream.of(1, 2), Stream.of(1, 3), Stream.of(2, 3), Stream.of(1, 2, 3)));
351     }
352 
353     // -- combinations(k)
354 
355     @Test
356     public void shouldComputeKCombinationsOfEmptyStream() {
357         assertThat(Stream.empty().combinations(1)).isEqualTo(Stream.empty());
358     }
359 
360     @Test
361     public void shouldComputeKCombinationsOfNonEmptyStream() {
362         assertThat(Stream.of(1, 2, 3).combinations(2))
363                 .isEqualTo(Stream.of(Stream.of(1, 2), Stream.of(1, 3), Stream.of(2, 3)));
364     }
365 
366     // -- flatMap
367 
368     @Test
369     public void shouldFlatMapInfiniteTraversable() {
370         assertThat(Stream.iterate(1, i -> i + 1).flatMap(i -> List.of(i, 2 * i)).take(7))
371                 .isEqualTo(Stream.of(1, 2, 2, 4, 3, 6, 4));
372     }
373 
374     // -- peek
375 
376     @Override
377     protected int getPeekNonNilPerformingAnAction() {
378         return 3;
379     }
380 
381     // -- permutations
382 
383     @Test
384     public void shouldComputePermutationsOfEmptyStream() {
385         assertThat(Stream.empty().permutations()).isEqualTo(Stream.empty());
386     }
387 
388     @Test
389     public void shouldComputePermutationsOfNonEmptyStream() {
390         assertThat(Stream.of(1, 2, 3).permutations()).isEqualTo(Stream.ofAll(Stream.of(Stream.of(1, 2, 3),
391                 Stream.of(1, 3, 2), Stream.of(2, 1, 3), Stream.of(2, 3, 1), Stream.of(3, 1, 2), Stream.of(3, 2, 1))));
392     }
393 
394     // -- appendSelf
395 
396     @Test
397     public void shouldRecurrentlyCalculateFibonacci() {
398         assertThat(Stream.of(1, 1).appendSelf(self -> self.zip(self.tail()).map(t -> t._1 + t._2)).take(10))
399                 .isEqualTo(Stream.of(1, 1, 2, 3, 5, 8, 13, 21, 34, 55));
400     }
401 
402     @Test
403     public void shouldRecurrentlyCalculatePrimes() {
404         assertThat(Stream
405                 .of(2)
406                 .appendSelf(self -> Stream
407                         .iterate(3, i -> i + 2)
408                         .filter(i -> self.takeWhile(j -> j * j <= i).forAll(k -> i % k > 0)))
409                 .take(10)).isEqualTo(Stream.of(2, 3, 5, 7, 11, 13, 17, 19, 23, 29));
410     }
411 
412     @Test
413     public void shouldDoNothingOnNil() {
414         assertThat(Stream.empty().appendSelf(self -> self)).isEqualTo(Stream.empty());
415     }
416 
417     @Test
418     public void shouldRecurrentlyCalculateArithmeticProgression() {
419         assertThat(Stream.of(1).appendSelf(self -> self.map(t -> t + 1)).take(4)).isEqualTo(Stream.of(1, 2, 3, 4));
420     }
421 
422     @Test
423     public void shouldRecurrentlyCalculateGeometricProgression() {
424         assertThat(Stream.of(1).appendSelf(self -> self.map(t -> t * 2)).take(4)).isEqualTo(Stream.of(1, 2, 4, 8));
425     }
426 
427     // -- containsSlice
428 
429     @Test
430     public void shouldRecognizeInfiniteDoesContainSlice() {
431         final boolean actual = Stream.iterate(1, i -> i + 1).containsSlice(of(12, 13, 14));
432         assertThat(actual).isTrue();
433     }
434 
435     // -- cycle
436 
437     @Test
438     public void shouldCycleEmptyStream() {
439         assertThat(empty().cycle()).isEqualTo(empty());
440     }
441 
442     @Test
443     public void shouldCycleNonEmptyStream() {
444         assertThat(of(1, 2, 3).cycle().take(9)).isEqualTo(of(1, 2, 3, 1, 2, 3, 1, 2, 3));
445     }
446 
447     // -- cycle(int)
448 
449     @Test
450     public void shouldCycleTimesEmptyStream() {
451         assertThat(empty().cycle(3)).isEqualTo(empty());
452     }
453 
454     @Test
455     public void shouldCycleTimesNonEmptyStream() {
456         assertThat(of(1, 2, 3).cycle(-1)).isEqualTo(empty());
457         assertThat(of(1, 2, 3).cycle(0)).isEqualTo(empty());
458         assertThat(of(1, 2, 3).cycle(1)).isEqualTo(of(1, 2, 3));
459         assertThat(of(1, 2, 3).cycle(3)).isEqualTo(of(1, 2, 3, 1, 2, 3, 1, 2, 3));
460     }
461 
462     // -- dropRight
463 
464     @Test
465     public void shouldLazyDropRight() {
466         assertThat(Stream.from(1).takeUntil(i -> i == 18).dropRight(7)).isEqualTo(Stream.range(1, 11));
467     }
468 
469     // -- extend
470 
471     @Test
472     public void shouldExtendStreamWithConstantValue() {
473         assertThat(Stream.of(1, 2, 3).extend(42).take(6)).isEqualTo(of(1, 2, 3, 42, 42, 42));
474     }
475 
476     @Test
477     public void shouldExtendStreamWithSupplier() {
478         assertThat(Stream.of(1, 2, 3).extend(() -> 42).take(6)).isEqualTo(of(1, 2, 3, 42, 42, 42));
479     }
480 
481     @Test
482     public void shouldExtendStreamWithFunction() {
483         assertThat(Stream.of(1, 2, 3).extend(i -> i + 1).take(6)).isEqualTo(of(1, 2, 3, 4, 5, 6));
484     }
485 
486     @Test
487     public void shouldExtendEmptyStreamWithConstantValue() {
488         assertThat(Stream.of().extend(42).take(6)).isEqualTo(of(42, 42, 42, 42, 42, 42));
489     }
490 
491     @Test
492     public void shouldExtendEmptyStreamWithSupplier() {
493         assertThat(Stream.of().extend(() -> 42).take(6)).isEqualTo(of(42, 42, 42, 42, 42, 42));
494     }
495 
496     @Test
497     public void shouldReturnAnEmptyStreamWhenExtendingAnEmptyStreamWithFunction() {
498         assertThat(Stream.<Integer> of().extend(i -> i + 1)).isEqualTo(of());
499     }
500 
501     @Test
502     public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithConstantValue() {
503         assertThat(Stream.continually(1).extend(42).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1));
504     }
505 
506     @Test
507     public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithSupplier() {
508         assertThat(Stream.continually(1).extend(() -> 42).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1));
509     }
510 
511     @Test
512     public void shouldReturnTheOriginalStreamWhenTryingToExtendInfiniteStreamWithFunction() {
513         assertThat(Stream.continually(1).extend(i -> i + 1).take(6)).isEqualTo(of(1, 1, 1, 1, 1, 1));
514     }
515 
516     // -- isLazy
517 
518     @Override
519     @Test
520     public void shouldVerifyLazyProperty() {
521         assertThat(empty().isLazy()).isTrue();
522         assertThat(of(1).isLazy()).isTrue();
523     }
524 
525     // -- subSequence(int, int)
526 
527     @Ignore
528     @Override
529     @Test
530     public void shouldReturnSameInstanceIfSubSequenceStartsAtZeroAndEndsAtLastElement() {
531         // Stream is lazy
532     }
533 
534     // -- tail
535 
536     @Test
537     public void shouldEvaluateTailAtMostOnce() {
538         final int[] counter = { 0 };
539         final Stream<Integer> stream = Stream.continually(() -> counter[0]++);
540         // this test ensures that the `tail.append(100)` does not modify the tail elements
541         final Stream<Integer> tail = stream.tail().append(100);
542         final String expected = stream.drop(1).take(3).mkString(",");
543         final String actual = tail.take(3).mkString(",");
544         assertThat(expected).isEqualTo("1,2,3");
545         assertThat(actual).isEqualTo(expected);
546     }
547 
548     @Test
549     public void shouldNotProduceStackOverflow() {
550         Stream.range(0, 1_000_000)
551                 .map(String::valueOf)
552                 .foldLeft(Stream.<String> empty(), Stream::append)
553                 .mkString();
554     }
555 
556     @Test // See #327, #594
557     public void shouldNotEvaluateHeadOfTailWhenCallingIteratorHasNext() {
558 
559         final Integer[] vals = new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
560 
561         final CheckedFunction2<StringBuilder, Integer, Void> doStuff = (builder, i) -> {
562             builder.append(i);
563             if (i == 5) {
564                 throw new Exception("Some error !!!");
565             } else {
566                 return null;
567             }
568         };
569 
570         final StringBuilder actual = new StringBuilder();
571         final CheckedFunction1<Integer, Void> consumer1 = doStuff.apply(actual);
572         Stream.of(vals)
573                 .map(v -> Try.run(() -> consumer1.apply(v)))
574                 .find(Try::isFailure)
575                 .getOrElse(() -> Try.success(null));
576 
577         final StringBuilder expected = new StringBuilder();
578         final CheckedFunction1<Integer, Void> consumer2 = doStuff.apply(expected);
579         java.util.stream.Stream.of(vals)
580                 .map(v -> Try.run(() -> consumer2.apply(v)))
581                 .filter(Try::isFailure)
582                 .findFirst()
583                 .orElseGet(() -> Try.success(null));
584 
585         assertThat(actual.toString()).isEqualTo(expected.toString());
586     }
587 
588     // -- take
589 
590     @Test
591     public void shouldNotEvaluateNplusOneWhenTakeN() {
592         final Predicate<Integer> hiddenThrow = i -> {
593             if (i == 0) {
594                 return true;
595             } else {
596                 throw new IllegalArgumentException();
597             }
598         };
599         assertThat(Stream.from(0).filter(hiddenThrow).take(1).sum().intValue()).isEqualTo(0);
600     }
601 
602     @Ignore
603     @Override
604     @Test
605     public void shouldReturnSameInstanceIfTakeAll() {
606         // the size of a possibly infinite stream is unknown
607     }
608 
609     // -- toStream
610 
611     @Test
612     public void shouldReturnSelfOnConvertToStream() {
613         final Value<Integer> value = of(1, 2, 3);
614         assertThat(value.toStream()).isSameAs(value);
615     }
616 
617     // -- toString
618 
619     @Test
620     public void shouldStringifyNil() {
621         assertThat(empty().toString()).isEqualTo("Stream()");
622     }
623 
624     @Test
625     public void shouldStringifyNonNil() {
626         assertThat(of(1, 2, 3).toString()).isEqualTo("Stream(1, ?)");
627     }
628 
629     @Test
630     public void shouldStringifyNonNilEvaluatingFirstTail() {
631         final Stream<Integer> stream = this.of(1, 2, 3);
632         stream.tail(); // evaluates second head element
633         assertThat(stream.toString()).isEqualTo("Stream(1, 2, ?)");
634     }
635 
636     @Test
637     public void shouldStringifyNonNilAndNilTail() {
638         final Stream<Integer> stream = this.of(1);
639         stream.tail(); // evaluates empty tail
640         assertThat(stream.toString()).isEqualTo("Stream(1)");
641     }
642 
643     // -- transform()
644 
645     @Test
646     public void shouldTransform() {
647         String transformed = of(42).transform(v -> String.valueOf(v.get()));
648         assertThat(transformed).isEqualTo("42");
649     }
650 
651     // -- unfold
652 
653     @Test
654     public void shouldUnfoldRightToEmpty() {
655         assertThat(Stream.unfoldRight(0, x -> Option.none())).isEqualTo(empty());
656     }
657 
658     @Test
659     public void shouldUnfoldRightSimpleStream() {
660         assertThat(
661                 Stream.unfoldRight(10, x -> x == 0
662                                             ? Option.none()
663                                             : Option.of(new Tuple2<>(x, x - 1))))
664                 .isEqualTo(of(10, 9, 8, 7, 6, 5, 4, 3, 2, 1));
665     }
666 
667     @Test
668     public void shouldUnfoldLeftToEmpty() {
669         assertThat(Stream.unfoldLeft(0, x -> Option.none())).isEqualTo(empty());
670     }
671 
672     @Test
673     public void shouldUnfoldLeftSimpleStream() {
674         assertThat(
675                 Stream.unfoldLeft(10, x -> x == 0
676                                            ? Option.none()
677                                            : Option.of(new Tuple2<>(x - 1, x))))
678                 .isEqualTo(of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
679     }
680 
681     @Test
682     public void shouldUnfoldToEmpty() {
683         assertThat(Stream.unfold(0, x -> Option.none())).isEqualTo(empty());
684     }
685 
686     @Test
687     public void shouldUnfoldSimpleStream() {
688         assertThat(
689                 Stream.unfold(10, x -> x == 0
690                                        ? Option.none()
691                                        : Option.of(new Tuple2<>(x - 1, x))))
692                 .isEqualTo(of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
693     }
694 
695     // -- Serializable
696 
697     @Test(expected = InvalidObjectException.class)
698     public void shouldNotSerializeEnclosingClassOfCons() throws Throwable {
699         Serializables.callReadObject(Stream.cons(1, Stream::empty));
700     }
701 
702     @Test(expected = InvalidObjectException.class)
703     public void shouldNotDeserializeStreamWithSizeLessThanOne() throws Throwable {
704         try {
705             /*
706              * This implementation is stable regarding jvm impl changes of object serialization. The index of the number
707              * of Stream elements is gathered dynamically.
708              */
709             final byte[] listWithOneElement = Serializables.serialize(Stream.of(0));
710             final byte[] listWithTwoElements = Serializables.serialize(Stream.of(0, 0));
711             int index = -1;
712             for (int i = 0; i < listWithOneElement.length && index == -1; i++) {
713                 final byte b1 = listWithOneElement[i];
714                 final byte b2 = listWithTwoElements[i];
715                 if (b1 != b2) {
716                     if (b1 != 1 || b2 != 2) {
717                         throw new IllegalStateException("Difference does not indicate number of elements.");
718                     } else {
719                         index = i;
720                     }
721                 }
722             }
723             if (index == -1) {
724                 throw new IllegalStateException("Hack incomplete - index not found");
725             }
726             /*
727              * Hack the serialized data and fake zero elements.
728 			 */
729             listWithOneElement[index] = 0;
730             Serializables.deserialize(listWithOneElement);
731         } catch (IllegalStateException x) {
732             throw (x.getCause() != null) ? x.getCause() : x;
733         }
734     }
735 
736     // -- spliterator
737 
738     @Test
739     public void shouldNotHaveSizedSpliterator() {
740         assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED)).isFalse();
741     }
742 
743 
744     @Test
745     public void shouldReturnSizeWhenSpliterator() {
746         assertThat(of(1, 2, 3).spliterator().getExactSizeIfKnown()).isEqualTo(-1);
747     }
748 
749 }